/******************************************************************************

    Copyright (C) 2006 Alessio Stalla (alessiostalla@gmail.com)

    This file is part of PrettyProlog.

    PrettyProlog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    PrettyProlog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrettyProlog; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    See the file COPYING for further details.

 *****************************************************************************/

package prettyprolog.engine.syspreds;

import java.util.List;
import java.util.Vector;

import prettyprolog.datatypes.Callable;
import prettyprolog.datatypes.Constant;
import prettyprolog.datatypes.Real;
import prettyprolog.datatypes.Substitution;
import prettyprolog.datatypes.Term;
import prettyprolog.datatypes.Variable;
import prettyprolog.engine.Domain;
import prettyprolog.engine.Engine;
import prettyprolog.engine.Function;
import prettyprolog.engine.FunctionException;
import prettyprolog.engine.UnificationAlgorithm;

/**
 * Implements the "is" system predicate. It succeeds when its second argument is a number or a constant and it can
 * be unified with the first. The second argument can be a function call, in which case it is evaluated.
 */
public class Is extends SystemPredicate {

	private static final Is inst = new Is();

	private Is() {
		super(2);
	}

	/** Returns the only instance of this system predicate. */
	public static Is getInstance() {
		return inst;
	}

	public SyspredEffect doCall(Engine en, Substitution subst, List<Term> args)	throws SyspredException {
		UnificationAlgorithm unif = en.getUnificationAlgorithm();
//		en.getIOManager().output("TERM: "+args.toString()+"\n");
		Term t = eval(args.get(1), en);
//		en.getIOManager().output("t: "+t+"\n");
		if(!t.isGround()) {
			throw new SyspredException("Arguments are not sufficiently instantiated: " + args.get(1));
		}
		Variable var = null;
		Vector dom = new Vector();
		if (args.get(0) instanceof Variable) {
			var = ((Variable) args.get(0));
			dom = Domain.getDomain(var);
//			en.getIOManager().output("Var: "+var+"\n");
//			en.getIOManager().output("Dom: "+dom+"\n");
		}
		Substitution s2 = unif.unify(args.get(0), t);
		//		en.getIOManager().output("s2: "+s2+"\n");
		if (s2 == null) {
			return new SyspredEffect(true, subst);
		} else {
			return new SyspredEffect(false, subst.compose(s2));
/*			int temp = (int) ((Real) t).getValue();
			if (dom.contains(temp)) {
				return new SyspredEffect(false, subst.compose(s2));
			} else {
				return new SyspredEffect(true, subst);
			}*/
		}
	}

	protected Term eval(Term expression, Engine en) throws SyspredException {
		if(expression instanceof Callable) {
			Callable c = (Callable) expression;
			Function f = en.getTheory().getFunction(c.getSymbol());
			if(f != null) {
				Term[] args = new Term[c.getArgCount()];
				int i = 0;
				for(Term t : c.getArgs()) {
					args[i++] = eval(t, en);
				}
				try {
					return f.apply(args);
				} catch(FunctionException e) {
					throw new SyspredException("Exception in function call " + expression, e);
				}
			} else {
				throw new SyspredException("Not a function: " + c.getSymbol() + " in " + expression);
			}
		} else if(expression instanceof Real || expression instanceof Constant) {
			return expression;
		} else {
			throw new SyspredException("Arithmetic exception: not a number or constant: " + expression);
		}
	}
	
	public boolean isBacktrackable() {
		return false;
	}

	public String toString() {
		return super.toString() + " (is/2)";
	}
}